﻿
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSQLObjects.Persistence;
using NSQLObjectsTests.DBModel;

namespace NSQLObjectsTests
{
    [TestClass]
    public class TestSet1
    {
        [ClassInitialize()]
        public static void TestSet1Initialize(TestContext testContext) {
            DefaultSetup.Connect();
        }


        [TestMethod]
        public void CreateCustomer() {
            Guid newId = NewCustomer();
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
            }
        }


        /// <summary>
        /// Test basic CRUD methods and immutable property (ID)
        /// </summary>
        [TestMethod]
        public void CRUDCustomer() {
            Guid newId = NewCustomer();
            Customer cc = new Customer() { ID = newId };
            string address = null;
            using (var ctx = SqlDataContext.CreateContext()) {
                Assert.AreEqual(RowStates.Untracked, ctx.GetRowState(cc));
                cc.Load(ctx);
                Assert.IsTrue(cc.IsLoaded);

                Assert.AreEqual(RowStates.Unchanged, ctx.GetRowState(cc));
                cc.ID = Guid.NewGuid();  // This should not change
                address = cc.Address;
                cc.Address += " (changed)";
                cc.Version = DateTime.Now.Ticks;
                Assert.AreEqual(RowStates.Modified, ctx.GetRowState(cc));
                ctx.SubmitChanges();
                Assert.AreEqual(RowStates.Unchanged, ctx.GetRowState(cc));
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
                Assert.AreEqual(ccNew.ID, newId);
                Assert.AreNotEqual(ccNew.Address, address);

                // Delete record
                ctx.Delete(ccNew);
                Assert.AreEqual(RowStates.Deleted, ctx.GetRowState(ccNew));
                ctx.SubmitChanges();
                Assert.AreEqual(RowStates.Untracked, ctx.GetRowState(ccNew));
            }

            // Check that record is deleted
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsFalse(ccNew.IsLoaded);
            }
        }

        [TestMethod()]
        public void NewDeleteTest() {
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer cc = new Customer() { ID = Guid.NewGuid() };
                ctx.Insert(cc);
                Assert.AreEqual(1, ctx.GetChangedRows().Count);
                ctx.Delete(cc);
                Assert.AreEqual(0, ctx.GetChangedRows().Count);
            }
        }


        [TestMethod]
        [ExpectedException(typeof(NSQLObjects.Exceptions.UpdateConflictException))]
        public void ConcurencyUpdateTest() {
            Guid newId = NewCustomer();
            Customer cc1 = new Customer() { ID = newId };
            Customer cc2 = new Customer() { ID = newId };
            using (var ctx = SqlDataContext.CreateContext()) {
                cc1.Load(ctx);
                Assert.IsTrue(cc1.IsLoaded);
                using (var ctx2 = SqlDataContext.CreateContext()) {
                    cc2.Load(ctx2);
                    Assert.IsTrue(cc2.IsLoaded);
                    cc2.Address += " (changed)";
                    cc2.Version = DateTime.Now.Ticks;
                    ctx2.SubmitChanges();
                }
                cc1.Address += " (changed 2)";
                cc1.Version = DateTime.Now.Ticks;
                ctx.SubmitChanges();
            }
        }

        [TestMethod]
        [ExpectedException(typeof(NSQLObjects.Exceptions.UpdateConflictException))]
        public void ConcurencyDeleteTest() {
            Guid newId = NewCustomer();
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer cc1 = new Customer() { ID = newId };
                cc1.Load(ctx);
                Assert.IsTrue(cc1.IsLoaded);
                using (var ctx2 = SqlDataContext.CreateContext()) {
                    Customer cc2 = new Customer() { ID = newId };
                    cc2.Load(ctx2);
                    Assert.IsTrue(cc2.IsLoaded);
                    cc2.Address += " (changed)";
                    cc2.Version = DateTime.Now.Ticks;
                    ctx2.SubmitChanges();
                }
                ctx.Delete(cc1);
                ctx.SubmitChanges();
            }
        }

        [TestMethod]
        public void PartialUpdateTest() {
            Guid newId = NewCustomer();
            CustomerAddress ca = new CustomerAddress() { ID = newId };
            using (var ctx = SqlDataContext.CreateContext()) {
                ca.Load(ctx);
                Assert.IsTrue(ca.IsLoaded);
                ca.Address += " (changed)";
                ca.Name += " (changed)";    // This should not change
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
                Assert.AreEqual(ccNew.Address, ca.Address);
                Assert.AreNotEqual(ccNew.Name, ca.Name);
            }
        }

        [TestMethod]
        public void AttachTest1() {
            Guid newId = NewCustomer();
            CustomerAddress ca = new CustomerAddress() { ID = newId };
            using (var ctx = SqlDataContext.CreateContext()) {
                ca.Load(ctx);
                Assert.IsTrue(ca.IsLoaded);
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                ctx.Attach(ca);
                ca.Address += " (changed)";
                ca.Name += " (changed)";    // This should not change
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
                Assert.AreEqual(ccNew.Address, ca.Address);
                Assert.AreNotEqual(ccNew.Name, ca.Name);
            }
        }

        [TestMethod]
        public void AttachTest2() {
            Guid newId = NewCustomer();
            Customer cc = new Customer() { ID = newId };
            using (var ctx = SqlDataContext.CreateContext()) {
                cc.Load(ctx);
                Assert.IsTrue(cc.IsLoaded);
            }
            CustomerAddress caNew = new CustomerAddress() { ID = newId, Address = cc.Address + " (attached)" };
            CustomerAddress caOld = new CustomerAddress() { ID = newId }; // Only compare values required here

            using (var ctx = SqlDataContext.CreateContext()) {
                ctx.Attach(caNew, caOld);
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
                Assert.AreEqual(ccNew.Address, caNew.Address);
            }
        }

        [TestMethod]
        public void DetachTest() {
            Guid newId = NewCustomer();
            CustomerAddress ca = new CustomerAddress() { ID = newId };
            using (var ctx = SqlDataContext.CreateContext()) {
                ca.Load(ctx);
                Assert.IsTrue(ca.IsLoaded);
                ca.Address += " (changed)";
                ctx.Detach(ca);
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer ccNew = new Customer() { ID = newId };
                ccNew.Load(ctx);
                Assert.IsTrue(ccNew.IsLoaded);
                Assert.AreNotEqual(ccNew.Address, ca.Address);
            }
        }

        [TestMethod()]
        [ExpectedException(typeof(NSQLObjects.Exceptions.SequenceException))]
        public void InvalidAttachTest() {
            Customer cc = new Customer() { };
            using (var ctx = SqlDataContext.CreateContext()) {
                ctx.Insert(cc);
                ctx.Insert(cc);
            }
        }

        private static Guid NewCustomer() {
            Random rnd = new Random();
            Guid newId = Guid.NewGuid();
            using (var ctx = SqlDataContext.CreateContext()) {
                Customer cc = new Customer();
                cc.ID = newId;
                cc.Version = 1;
                cc.Name = FirstNames[rnd.Next(FirstNames.Length)] + ", " + LastNames[rnd.Next(LastNames.Length)];
                cc.Address = "To be filled in";
                ctx.Insert(cc);
                ctx.SubmitChanges();
            }
            return newId;
        }


        public static readonly string[] FirstNames = { "Anyromil", "Arior", "Atafarys", "Atyziru", "Biras", "Cudirot", "Emeki", "Hiyi", "Icbod", "Ixrak", "Kadob", 
            "Kosot", "Oheza", "Rehod", "Rexlo", "Roke", "Rolonoil", "Uhier", "Zixiv" };

        public static readonly string[] LastNames = { "Clawtwister", "Dragoncaster", "Lonejackal", "Redroot", "Wildfalcon", "Bleakmask", "Scarshot", "Bleakrider",
            "Clawslayer", "Chaosnest", "Banebeak", "Warriortaker", "Moonfang", "Blooddusk", "Tailscribe", "Chaosmaw", "Moonworm", "Spelllance", 
            "Silverraven", "Starwanderer" };


    }
}
